// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

namespace SnapObjects.Data
{
    internal class SqlExpressionBuilder
    {
        private readonly ISqlExpressionContext _expressionContext;

        public SqlExpressionBuilder(ISqlExpressionContext expressionContext)
        {
            _expressionContext = expressionContext;
        }

        //public ISqlExpressionContext ExpressionContext => _expressionContext;

        public string BuildSqlExpression(
            string expression,
            bool buildSubQueris = false,
            bool buildParameters = false)
        {
            var sqlTokenizer = new ModelSqlTokenizer(expression);

            if (buildSubQueris)
            {
                this.BuildSubQuery(sqlTokenizer);
            }

            if (buildParameters)
            {
                this.BuildSqlParameters(sqlTokenizer);
            }

            this.BuildProertyRef(sqlTokenizer);

            return sqlTokenizer.GetSql();
        }

        private void BuildSqlParameters(ModelSqlTokenizer sqlTokenizer)
        {
            var parameters = _expressionContext.ModelMeta.SqlParameters;

            foreach (var parameter in parameters)
            {
                var placeHold = _expressionContext.GetParameterPlacehold(parameter.Name);
                sqlTokenizer.ReplaceParameter(parameter.Name, placeHold);
            }
        }

        private void BuildSubQuery(ModelSqlTokenizer sqlTokenizer)
        {
            foreach (var subQuery in _expressionContext.ModelMeta.SubQueries)
            {
                var sql = subQuery.RawSql;

                if (!subQuery.IsRawSql)
                {
                    sql = _expressionContext.BuildEmbeddedModel(subQuery.ModelType, subQuery.SelectName);
                }

                var subQueryTokenizer = new ModelSqlTokenizer(sql);

                this.BuildSqlParameters(subQueryTokenizer);
                this.BuildProertyRef(subQueryTokenizer);

                var subQuerySql = "(" + subQueryTokenizer.GetSql() + ")";

                sqlTokenizer.ReplaceSubQuery(subQuery.Name, subQuerySql);
            }
        }

        private void BuildProertyRef(ModelSqlTokenizer sqlTokenizer)
        {
            var properties = sqlTokenizer.GetPropertyRefs();

            foreach (var property in properties)
            {
                var modelProperties = _expressionContext.ModelMeta.PropertyMap;

                if (modelProperties.TryGetValue(property, out var propertyMeta))
                {
                    var propertyExp = _expressionContext.BuildPropertyRef(property);
                    sqlTokenizer.ReplaceProperty(property, propertyExp);
                }
            }
        }
    }
}
